import logging
from pathlib import (
    Path,
)
from sys import (
    stdout,
)
from numpy.random import (
    default_rng,
)
from scipy import (
    constants,
)
from tensorflow import (
    get_logger as tf_get_logger,
)

from src.config.config_error_model import (
    ConfigErrorModel,
)
'''
from src.config.config_td3_learner import (
    ConfigTD3Learner,
)
'''

from src.config.config_sac_learner import (
    ConfigSACLearner,
)
from src.data.channel.los_channel_model import (
    los_channel_model,
)
from src.utils.get_wavelength import (
    get_wavelength,
)


class Config:
    """
    配置类用于设置所有全局参数。
    """

    def __init__(
            self,
    ) -> None:
        """
        初始化 Config 实例，设置基本参数和路径，并初始化学习器配置。
        """
        self._pre_init()

        # 一般设置
        self.profile: bool = False  # 性能分析
        self.show_plots: bool = True  # 是否显示图形

        self.verbosity: int = 1  # 详细输出级别，0 = 无输出，1 = 有输出
        self._logging_level_stdio = logging.INFO  # 标准输出日志级别
        self._logging_level_file = logging.WARNING  # 文件日志级别
        self._logging_level_tensorflow = logging.WARNING  # TensorFlow 日志级别

        # 基本通信参数
        self.freq: float = 2 * 10**9  # 频率
        self.noise_power_watt: float = 10**(7 / 10) * 290 * constants.value('Boltzmann constant') * 30 * 10**6  # 噪声功率
        self.power_constraint_watt = 100  # 功率限制，单位为瓦特

        self.wavelength: float = get_wavelength(self.freq)  # 波长

        # 轨道参数
        self.altitude_orbit: float = 600 * 10**3  # 轨道高度 d0
        self.radius_earth: float = 6378.1 * 10**3  # 地球半径 RE，地心坐标系下

        self.radius_orbit: float = self.altitude_orbit + self.radius_earth  # 地心坐标系下的轨道半径 r0

        # 用户参数
        self.user_nr: int = 3  # 用户数量
        self.user_gain_dBi: float = 0  # 用户增益，单位 dBi
        self.user_dist_average: float = 1_000  # 平均用户距离，单位米
        self.user_dist_bound: float = 30  # 用户距离的方差，均匀分布 [avg-bound, avg+bound]
        self.user_center_aod_earth_deg: float = 90  # 用户的平均中心方向，单位度

        self.user_gain_linear: float = 10**(self.user_gain_dBi / 10)  # 用户增益，线性值

        # 卫星参数
        self.sat_nr: int = 2  # 卫星数量
        self.sat_tot_ant_nr: int = 4  # 总发射天线数量，应该大于卫星数量
        self.sat_gain_dBi: float = 20  # 卫星增益，单位 dBi，待确认
        self.sat_dist_average: float = 10_000  # 平均卫星距离，单位米
        self.sat_dist_bound: float = 0  # 卫星距离的方差，均匀分布 [avg-bound, avg+bound]
        self.sat_center_aod_earth_deg: float = 90  # 卫星的平均中心方向，单位度

        self.sat_gain_linear: float = 10**(self.sat_gain_dBi / 10)  # 卫星增益，线性值
        self.sat_ant_nr: int = int(self.sat_tot_ant_nr / self.sat_nr)  # 每颗卫星的发射天线数量
        self.sat_ant_gain_linear: float = self.sat_gain_linear / self.sat_tot_ant_nr  # 每个卫星天线的增益，线性值
        self.sat_ant_dist: float = 3 * self.wavelength / 2  # 天线元件之间的距离，单位米

        # 信道模型
        self.channel_model = los_channel_model  # 选择的信道模型

        # 学习器配置
        # self.config_learner = ConfigTD3Learner(size_state=self.sat_nr*self.user_nr,
        #                                        num_actions=2*self.sat_nr*self.sat_ant_nr*self.user_nr)
        self.config_learner = ConfigSACLearner(
            # size_state=self.sat_nr*self.user_nr,
            size_state=2*self.sat_nr*self.sat_ant_nr*self.user_nr,
            num_actions=2*self.sat_nr*self.sat_ant_nr*self.user_nr,
        )

        self._post_init()

    def _pre_init(
            self,
    ) -> None:
        """
        初始化前设置路径和随机数生成器。
        """
        self.rng = default_rng()  # 默认随机数生成器
        self.logger = logging.getLogger()  # 获取日志记录器

        # 设置项目根路径
        self.project_root_path = Path(__file__).parent.parent.parent
        self.performance_profile_path = Path(self.project_root_path, 'outputs', 'performance_profiles')
        self.output_metrics_path = Path(self.project_root_path, 'outputs', 'metrics')
        self.trained_models_path = Path(self.project_root_path, 'models')

        # 创建必要的文件夹
        self.performance_profile_path.mkdir(parents=True, exist_ok=True)
        self.output_metrics_path.mkdir(parents=True, exist_ok=True)
        self.trained_models_path.mkdir(parents=True, exist_ok=True)

    def _post_init(
            self,
    ) -> None:
        """
        初始化后进一步配置错误模型、日志记录和参数收集。
        """
        # 错误模型
        self.error_model = ConfigErrorModel()  # 初始化错误模型配置

        # 日志记录设置
        self.logfile_path = Path(self.project_root_path, 'outputs', 'logs', 'log.txt')
        self.__logging_setup()  # 设置日志记录

        # 收集参数
        self.satellite_args: dict = {
            'rng': self.rng,
            'antenna_nr': self.sat_ant_nr,
            'antenna_distance': self.sat_ant_dist,
            'antenna_gain_linear': self.sat_ant_gain_linear,
            'freq': self.freq,
            'center_aod_earth_deg': self.sat_center_aod_earth_deg,
        }

        self.user_args: dict = {
            'gain_linear': self.user_gain_linear,
        }

        self.mmse_args: dict = {
            'power_constraint_watt': self.power_constraint_watt,
            'noise_power_watt': self.noise_power_watt,
            'sat_nr': self.sat_nr,
            'sat_ant_nr': self.sat_ant_nr,
        }

        self.mrc_args: dict = {
            'power_constraint_watt': self.power_constraint_watt,
        }

    def __logging_setup(
            self,
    ) -> None:
        """
        设置日志记录，包括格式和处理器。
        """
        # 设置日志格式
        logging_formatter = logging.Formatter(
            '{asctime} : {levelname:8s} : {name:30} : {funcName:20s} :: {message}',
            datefmt='%Y-%m-%d %H:%M:%S',
            style='{',
        )

        # 创建日志处理器
        logging_file_handler = logging.FileHandler(self.logfile_path)  # 文件处理器
        logging_stdio_handler = logging.StreamHandler(stdout)  # 标准输出处理器

        # 设置日志级别
        logging_file_handler.setLevel(self._logging_level_file)  # 文件日志级别
        logging_stdio_handler.setLevel(self._logging_level_stdio)  # 标准输出日志级别

        tensorflow_logger = tf_get_logger()  # 获取 TensorFlow 日志记录器
        tensorflow_logger.setLevel(self._logging_level_tensorflow)  # TensorFlow 日志级别

        self.logger.setLevel(logging.NOTSET)  # 设置主日志记录器的级别为最低，以捕获所有日志

        # 设置格式
        logging_file_handler.setFormatter(logging_formatter)
        logging_stdio_handler.setFormatter(logging_formatter)

        # 添加处理器到主日志记录器
        self.logger.addHandler(logging_file_handler)
        self.logger.addHandler(logging_stdio_handler)

        # 检查日志文件大小
        large_log_file_size = 30_000_000  # 大日志文件的大小（30MB）
        if self.logfile_path.stat().st_size > large_log_file_size:
            self.logger.warning(f'log file size >{large_log_file_size / 1_000_000} MB')  # 警告日志文件过大
